import os  # 导入os模块，用于处理文件和目录
import cv2 as cv  # 导入cv2模块，用于图像处理
import numpy as np  # 导入numpy模块，用于处理大型多维数组和矩阵
from zipfile import ZipFile  # 从zipfile模块导入ZipFile，用于处理ZIP文件
from tkinter import Tk, Button, filedialog, Scale, Label, OptionMenu, StringVar, Toplevel, ttk, messagebox  # 导入tkinter模块，用于创建图形用户界面
from threading import Thread, Event  # 从threading模块导入Thread和Event，用于创建和管理线程
from util import process_images_from_zip  # 从util模块导入process_images_from_zip函数，用于处理ZIP文件中的图像
from util import load_config # 从util模块导入load_config函数，用于加载配置文件
import logging  # 导入logging模块，用于记录日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(thread)d - %(message)s') # 配置日志记录器，设置日志级别、日志格式和日志输出位置
# %(thread)d 是需要打印的线程ID，这样可以区分不同的线程

class ImageProcessingApp:  # 定义一个名为ImageProcessingApp的类，用于创建图像处理应用
    
    # 首先定义一个初始化函数，用于初始化应用
    # 在这个函数中，我们将创建应用的根窗口，并设置应用的标题和默认窗口大小
    # 然后，我们将创建应用的图形用户界面组件，并将它们添加到应用窗口
    # 最后，我们将初始化应用的一些属性，如线程事件、进度值和ZIP文件路径
    def __init__(self, root): 
        config = load_config() # 从配置文件中加载配置

        logging.info('开始函数: __init__ 开始搭建APP的图形用户界面')  # 记录日志，表示开始初始化应用
        self.root = root  # 设置应用的根窗口
        self.root.title('Image Processing App')  # 设置应用的标题
        self.root.geometry('500x400')  # 设置应用的默认窗口大小

        # 创建一个按钮，用于选择ZIP文件
        self.zip_btn = Button(root, text="Select ZIP", command=self.select_zip)  
        self.zip_btn.pack()  # 将按钮添加到应用窗口

        # 创建一个标签，用于显示选择的文件
        self.selected_file_label = Label(root, text="Selected File: None")  
        self.selected_file_label.pack()  # 将标签添加到应用窗口

        # 创建一个标签，用于显示缩放范围
        self.scale_label = Label(root, text="Scale Range")  
        self.scale_label.pack()  # 将标签添加到应用窗口
        
        self.scale_min = Scale(root, from_=config['scale_min'], to=config['scale_middle'], resolution=0.1, orient="horizontal", label="Min")  # 创建一个滑块，用于设置最小缩放值
        self.scale_min.set(config['scale_min_default'])  # 设置滑块的默认值
        self.scale_min.pack()  # 将滑块添加到应用窗口

        self.scale_max = Scale(root, from_=config['scale_middle'], to=config['scale_max'], resolution=0.1, orient="horizontal", label="Max")  # 创建一个滑块，用于设置最大缩放值
        self.scale_max.set(config['scale_max_default'])  # 设置滑块的默认值
        self.scale_max.pack()  # 将滑块添加到应用窗口

        # 创建一个滑块，用于设置处理的图像数量
        self.num_images_scale = Scale(root, from_=config['num_images_min'], to=config['num_images_max'], orient="horizontal", label="Number of Images")  
        self.num_images_scale.set(config['num_images_default'])  # 设置滑块的默认值
        self.num_images_scale.pack()  # 将滑块添加到应用窗口

        # 创建一个选项菜单，用于选择输出的图像通道数，可以是RGB、Grayscale或Binary
        self.output_type = StringVar(root)  # 创建一个字符串变量，用于存储输出类型
        self.output_type.set(config['output_type_default'])  # 设置字符串变量的默认值
        self.option_menu = OptionMenu(root, self.output_type, *config['output_type_options'])  # 创建一个选项菜单，用于选择输出类型
        self.option_menu.pack()  # 将选项菜单添加到应用窗口

        # 创建一个按钮，用于开始处理图像
        self.process_btn = Button(root, text="Process", command=self.start_processing, state="disabled")  
        self.process_btn.pack()  # 将按钮添加到应用窗口

        # 创建一个按钮，用于取消处理图像
        self.cancel_btn = Button(root, text="Cancel", command=self.cancel_processing, state="disabled")  
        self.cancel_btn.pack()  # 将按钮添加到应用窗口
        
        # 添加用于显示进度的Label和StringVar
        self.progress_var = StringVar(root)  # 创建一个字符串变量，用于存储进度信息
        self.progress_label = Label(root, textvariable=self.progress_var)  # 创建一个标签，用于显示进度信息
        self.progress_label.pack()  # 将标签添加到应用窗口

        # 添加用于显示进度的进度条
        self.progress_bar = ttk.Progressbar(root, orient='horizontal', mode='determinate')  # 创建一个进度条，用于显示处理进度
        self.progress_bar.pack(fill='x')  # 将进度条添加到应用窗口

        # 初始化应用的一些属性
        self.thread_event = Event() # 创建一个线程事件，用于处理线程
        self.progress_value = 0  # 初始化进度值
        self.zip_path = None  # 初始化ZIP文件路径

    def select_zip(self):  # 定义一个函数，用于选择ZIP文件
        self.zip_path = filedialog.askopenfilename(filetypes=[("ZIP files", "*.zip")])  # 打开文件对话框，让用户选择ZIP文件
        if self.zip_path:  # 如果用户选择了文件
            logging.info(f'选择的zip文件: {self.zip_path}')  # 记录日志
            self.selected_file_label['text'] = "Selected File: " + os.path.basename(self.zip_path)  # 更新标签的文本，显示选择的文件
            self.process_btn.config(state="normal")  # 如果用户选择了文件，使处理按钮可用


    def threaded_image_processing(self):  # 定义一个函数，用于在新线程中处理图像
        # log the start of function
        logging.info('开始函数: threaded_image_processing')  # 记录日志，表示开始在新线程中处理图像
        
        if not self.zip_path:  # 如果没有选择ZIP文件
            return  # 结束函数
        
        # 提示用户正在进行处理
        self.progress_var.set("Processing images...")  # 更新进度信息
        self.cancel_btn.config(state="normal")  # 使取消按钮可用
        self.process_btn.config(state="disabled")  # 禁用处理按钮以避免重复点击
        
        # 更新图像处理参数，这些将是您从GUI控件获取的值
        scale_min = self.scale_min.get()  # 获取最小缩放值
        scale_max = self.scale_max.get()  # 获取最大缩放值
        num_images = self.num_images_scale.get()  # 获取处理的图像数量
        #log all the parameters
        logging.info(f'图像处理参数: scale_min={scale_min}, scale_max={scale_max}, num_images={num_images}')  # 记录日志，显示图像处理参数
        

        try:
            self.progress_value = 0  # 初始化进度值
            self.progress_bar['value'] = self.progress_value  # 更新进度条的值

            process_images_from_zip(self.zip_path, 
                                    scale_min=scale_min, 
                                    scale_max=scale_max, 
                                    num_images=num_images, 
                                    progress_callback=self.update_progress, 
                                    thread_event=self.thread_event, 
                                    output_type=self.output_type.get())  # 调用util.py中的函数处理图像
        except Exception as e:
            self.progress_var.set(f"Error: {e}")  # 如果出现异常，更新进度信息，显示错误信息
            return  # 结束函数

        # 结束处理后的操作
        self.progress_var.set("Processing completed!")  # 更新进度信息，表示处理完成
        logging.info('图像处理完成')  # 记录日志，表示图像处理完成
        self.process_btn.config(state="normal")  # 重新启用处理按钮
        self.cancel_btn.config(state="disabled")  # 禁用取消按钮

    def start_processing(self):  # 定义一个函数，用于开始处理图像
        logging.info('开始图像处理')  # 记录日志，表示开始处理图像
        self.thread_event.clear()  # 清除线程事件
        thread = Thread(target=self.threaded_image_processing)  # 创建一个线程，用于处理图像
        thread.daemon = True  # 设置线程为守护线程
        # 这种守护线程将在主线程结束时自动结束
        # 这样，当用户关闭应用时，守护线程也会结束
        # 比较适合这种需要在后台运行的操作，保持前台用户界面的响应性的操作
        
        thread.start()  # 启动线程

    
    def update_progress(self, progress):  # 定义一个函数，用于更新进度条的进度 
        logging.info(f'更新进度: {progress}')  # 记录日志，显示更新的进度
        self.progress_value = progress  # 更新进度值
        self.progress_bar['value'] = self.progress_value  # 更新进度条的值
        self.root.update_idletasks()  # 强制更新应用的图形用户界面，以显示进度条的更新


    def cancel_processing(self):  # 定义一个函数，用于取消处理图像
        logging.info('用户取消了图像处理')  # 记录日志，表示用户取消了图像处理
        self.thread_event.set()  # 设置线程事件

root = Tk()  # 创建一个Tk对象，作为应用的根窗口
app = ImageProcessingApp(root)  # 创建一个ImageProcessingApp对象，作为应用
root.mainloop()  # 启动应用的主循环


